Express框架学习
概述
Express框架是什么
Express 是一个基于 Node 平台的 web应用开发框架,它提供了一系列的强大特性
Express 框架核心特性:
- 可以设置中间件来响应 HTTP 请求。
- 定义了路由表用于执行不同的 HTTP 请求动作。
- 可以通过向模板传递参数来动态渲染 HTML 页面。
安装
npm install express --save
基本的使用例
// 引入模块
const express = require('express')
// 创建网站服务器
const app = express();
// 如果是 / 表示这个是根项目(和javaWeb 一样)
app.get('/',(req,res)=>{
// 响应方法是 send() 能够自动把 响应类型、类型编码、http状态码 等信息写到响应头里
res.send('Hello Express !')
})
// 监听端口
app.listen(3000);
console.log('网站服务器启动成功');
自动更新服务
nodemon 可以随时监听文件的变更,自动重启服务,总之就是 node 服务器开发用的工具
# 安装依赖
npm install -g nodemon
使用时只需使用它来启动服务就行了
nodemon index.js
当修改文件之后再保存会自动重启服务器,省的每次还需要手动重启
中间件
中间件基本使用
中间件就是一堆方法,可以接收客户端发来的请求、可以对请求做出响应,也可以将请求继续交给下一个中间件继续处理
中间件由两部分构成,中间方法以及请求处理函数
其中
- 中间方法由Express提供,负责拦截请求(就是get、post这些方法)
- 请求处理函数由开发人员提供,负责处理请求(就是get、post的回调函数)
app.get('请求路径','处理函数')
请求从上到下依次匹配中间件,一旦匹配成功,终止匹配
但是可以调用next方法将请求的控制权交给下一个中间件,直到遇到结束请求 send() 的中间件
app.get('/',(req,res,next)=>{
// 注意!! 如果要传递给下一个中间件就不能在这里 使用 res.send()
// 添加一个属性,可以传递给下面那个中间件
req.name = '张三'
next()
})
app.get('/',(req,res)=>{
res.send(req.name + 'this is 2')
})
use 中间件的用法
app.use
匹配所有的请求方式,可以直接传入请求处理函数,代表接收所有的请求
注意:虽然这个use
能接收到所有的请求,但是因为中间件是有顺序的,所以最好放在上面
app.use((req,res,next)=>{
// 这个 req.url 能接收到 所有发向这个端口的请求,包括没有组测中间件的请求
console.log(req.url);
console.log('这里是use方法');
next()
})
除了上面这种不写请求路径的写法,还可以指定请求路径,使所有访问这个请求路径的请求(get、post...)都经过它,但是也要注意顺序问题
app.use('/request', (req,res,next)=>{
console.log('这里是use方法');
next()
})
最后别忘了use中间件最后要加上一个next
否则会一直卡在这个中间这里
use 调用函数的用法
可以在use里传递一个函数来执行
const express = require('express');
// 创建网站服务器
const app = express();
app.use(fn({ a: 1 }));
// use 可以传递一个函数进行调用
function fn(obj) {
// 要返回一个函数
return function (req, res, next) {
if (obj.a == 1) {
console.log(req.url);
} else {
console.log(req.method);
}
next();
}
}
app.get('/', (req, res) => {
res.send('use 方法执行成功');
});
console.log('启动服务器');
app.listen(3000);
实际上就相当于直接写在里面
等价于上面的代码
const express = require('express');
// 创建网站服务器
const app = express();
app.use((req, res, next) => {
let obj = { a: 1 };
if (obj.a == 1) {
console.log(req.url);
} else {
console.log(req.method);
}
next();
})
app.get('/', (req, res) => {
res.send('use 方法执行成功');
})
console.log('启动服务器');
app.listen(3000);
中间件的应用
- 路由保护,客户端在访问需要登陆的页面时,可以先使用中间件判断用户登陆状态,用户如果未登录,则拦截请求,直接响应,禁止用户进入需要登陆的页面
app.use((req,res,next)=>{
let isLogin = false
if (isLogin) {
next()
}else{
res.send('未登录,请先登陆')
}
})
app.get('/',(req,res)=>{
res.send('登陆成功,可以直接访问这个页面')
})
网站维护公告,在所有路由的最上面定义接收所有请求的中间件,直接为客户端做出响应,网站正在维护中
自定义 404 页面(放在最下面就行了,表示前面的中间件都没有匹配成功)
app.use((req,res,next)=>{
// 为客户端响应定义404状态码以及提示信息
res.status(404).send('这是一个404页面')
})
错误处理中间件
在程序执行过程中,不可避免的会出现一些无法预料的错误,比如文件读取失败,数据库连接失败。错误处理中间件是一个 集中处理错误的地方
app.get('/',(req,res)=>{
throw new Error('程序发生了未知错误')
})
// 错误处理中间件函数的定义方式与其他中间件函数基本相同,差别在于错误处理函数有四个自变量而不是三个:(err, req, res, next)
app.use((err,req,res,next)=>{
res.status(500).send(err.message + "--服务器发生未知错误")
})
但是注意! 错误处理中间件 只能捕获到同步代码的错误,如果是异步代码发生了错误是无法捕获到的,这时需要使用 next()
方法 手动去触发错误处理中间件
// 这个fs是文件读取模块
const fs = require('fs')
app.get('/',(req,res,next)=>{
fs.readFile("/file-does-not-exist",(err,data)=>{
if(err){
next(err)
}
})
})
捕获错误
app.get('/',async (req,res,next)=>{
try {
await User.find({name: '张三'})
}catch(ex){
next(ex)
}
})
上面可以通过 next 传递给错误处理中间件
all和use的区别
all 执行完整匹配,use 只匹配前缀
app.use('/a', (req, res, next) => {
console.log('app.use')
next()
})
app.all('/a', (req, res, next) =>{
console.log('app.all')
next()
})
访问 /a
use 和 all 都会被调用;访问 /a/b
只有 use 被调用
Express 请求处理
路由模块
就是将二级路由放到其他文件管理,一级路由主文件管理
然后在主文件里把二级路由和一级路由绑定起来
普通的使用二级路由
const express = require('express');
// 创建网站服务器
const app = express();
// 创建路由对象 const home = express.Router();
// 将路由和请求路径进行匹配 app.use('/home', home);
// 在home路由下继续创建路由(二级路由) home.get('/index', (req, res) => { res.send('欢迎页'); });
console.log('启动服务器'); app.listen(3000)
> 构建路由模块
模块一
```js
// home.js
const home = express.Router();
home.get('/index', (req, res) => {
res.send('欢迎页');
});
module.exports = home;
模块二
// admin.js
const admin = express.Router();
admin.get('/index', (req, res) => {
res.send('欢迎页');
});
module.exports = admin;
使用上面的模块
// app.js
const home = require('./route/home.js');
const admin = require('./route/admin.js');
// 把二级路由和遗迹路由绑定起来
app.use('/home' , home);
app.use('/admin' , admin);
GET参数获取
Express框架中使用 req.query
即可获取GET参数,框架内部会将GET参数转换为对象并返回
const express = require('express');
// 创建网站服务器
const app = express();
// http://localhost:3000/?name=alsritter&gender=male
app.get('/', (req, res) => {
res.send(req.query)
})
console.log('启动服务器');
app.listen(3000)
POST参数获取
Express中接收post请求参数需要借助第三方包 body-parser
这个包也是官方的,但是官方为了缩小体积便将其抽离了出来
所以需要使用 npm 进行安装
npm install body-parser --save
const express = require('express');
const bodyParser = require('body-parser');
// 创建网站服务器
const app = express();
// 配置body-parser模块
// urlencoded:
// true 方法内部使用第三方模块qs处理请求参数的格式
// false 方法内部使用querystring模块处理请求参数的格式
app.use(express.urlencoded({ extended: true })); // 接收 x-www-form-urlencoded 数据
app.use(express.json()); // 接收 JSON 数据
// http://localhost:3000
// 通过postman 发post请求
app.post('/', (req, res) => {
res.send(req.body)
})
console.log('启动服务器');
app.listen(3000)
补:使用 Postman 发post请求需要在 Header 里设置
Key 设置为 Content-Type
value 设置为 application/json
然后再在 Body 里的 x-www-form-urlencoded 里写数据
参考资料 postman 中 form-data、x-www-form-urlencoded 的区别
指定请求参数
指定请求需要哪些参数
以 :
进行分隔,后面就是请求参数
传输数据也无需使用 ?
分隔,而是直接跟在后面,类似java的 RestFul 风格请求资源
接收数据使用的是 params
例:
const express = require('express');
// 创建网站服务器
const app = express();
// 以 : 进行分隔,后面就是请求参数
// 传输数据也无需使用 ? 分隔
// http://localhost:3000/find/10
app.get('/find/:id', (req, res) => {
// 接收数据使用的不是query而是params
res.send(req.params)
})
console.log('启动服务器');
app.listen(3000)
可以写多个参数
// http://localhost:3000/find/123/alsritter/20
app.get('/find/:id/:name/:age', (req, res) => {
res.send(req.params)
})
静态资源处理
通过 Express 内置的 express.static
可以方便的托管静态文件,例如img、css、JavaScript 文件等
express.static
就是指定一个静态资源的根目录,能直接访问到这个目录下的所有文件
const express = require('express');
// 引入路径处理包来拼接路径
const path = require('path')
// 创建网站服务器
const app = express();
// 实现静态资源访问功能
// http://localhost:3000/temp.txt 就能访问到 public/temp.txt 文件
app.use(express.static(path.join(__dirname,'public')))
console.log('启动服务器');
app.listen(3000)
还可以在静态资源路径前面再加一个路径(使用的是 use 自身的路径匹配)
// http://localhost:3000/static/temp.txt 就能访问到 public/temp.txt 文件
app.use('/static',express.static(path.join(__dirname,'public')))
响应数据
返回一个 JSON
直接使用 res.json()
就行了,这个方法会自动设置响应头
HTTP访问控制
参考资料
参考资料 MDN--HTTP访问控制(CORS) 参考资料 MDN--同源策略 参考资料 Fundebug--九种跨域方式实现原理 参考资料 阮一峰--浏览器同源政策及其规避方法 参考资料 NathanYangcn--跨域之二:JSONP 和 CORS
配置 CORS
Express 代码如下:
// 设置允许跨域访问该服务
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Headers', 'mytoken');
next();
});
方法一:修改请求头 对应 Cors
在3001服务器的请求响应头里加入 Access-Control-Allow-Origin
// 3001端口的服务 返回数据
let app2 = express();
app2.get('/', (rep, res) => {
res.header("Access-Control-Allow-Origin", "*");
res.send("你好")
})
app2.listen(3001)
方法二:传参函数名拼接调用 对应 jsonp
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>返回测试页面</h1>
<script>
function f(data){
alert(data)
}
</script>
<!-- 请求后台3001服务器时传入一个参数callback=f 注意这个 f 是前面定义的函数名 -->
<script src="http://localhost:3001?callback=f"></script>
</body>
</html>
const express = require('express');
// 3000端口的服务 将当前目录作为http服务
let app = express();
// 注意,当静态目录下有 index.html 默认将这个html返回出去
app.use(express.static(__dirname))
app.listen(3000)
// 3001端口的服务 返回数据
let app2 = express();
app2.get('/', (rep, res) => {
// 后台取得前台传入的那个参数
let fun = rep.query.callback;
// 把前台传过来的参数再拼接成一个函数传回去(就是拼接成一个函数调用)
res.send(fun + "('你好')");
})
app2.listen(3001)
就可以加载到数据了
CORS 的使用
参考资料 Express--cors 参考资料 MDN--HTTP访问控制(CORS)
首先安装
npm install cors --save
然后只需要在api服务端加入一个
app2.use(cors())
就可以了,如下
const express = require('express');
const cors = require('cors');
// 3000端口的服务 将当前目录作为http服务
let app = express();
// 注意,当静态目录下有 index.html 默认将这个html返回出去
app.use(express.static(__dirname))
app.listen(3000)
// 3001端口的服务 返回数据
let app2 = express();
app2.use(cors())
app2.get('/', (rep, res) => {
res.send("你好");
})
app2.listen(3001)
上面的 cors()
默认配置如下
{
"origin": "*",
"methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
"preflightContinue": false,
"optionsSuccessStatus": 204
}
express 跨域模板
如果不使用 cors 则是通过 header 来处理的 如下
express 跨域是直接通过设置header属性来完成的
- Access-Control-Allow-Origin 允许的域
- Access-Control-Allow-Headers 允许的header类型
- Access-Control-Allow-Methods 允许的Methods类型
// 设置允许跨域访问该服务
app.all('*', function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
//
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header('Access-Control-Allow-Headers', 'Content-Type');
res.header('Access-Control-Allow-Headers', 'mytoken');
next();
});
express-art-template 模板引擎
模板引擎 🌟
就是类似Vue的那样对数据进行绑定
参考资料 前端模版引擎---art-template-【下】 里面介绍了一些art-template语法
为了使 art-template 模板引擎能够更好的和 Express 框架配合,模板引擎官方在原 art-template 模板引擎的基础上再次封装了 express-art-template
所以安装时需要把两个都安装上
npm install art-template express-art-template --save
const express = require('express');
// 引入路径处理包来拼接路径
const path = require('path')
// 创建网站服务器
const app = express();
// 当渲染后缀为art的模板时 使用 express-art-template 引擎
app.engine('art', require('express-art-template'));
// 设置模板存放目录
// 第一个参数是固定的
app.set('views', path.join(__dirname, 'views'));
// 设置express框架默认后缀为 art
app.set('view engine', 'art');
app.get('/', (req, res) => {
// 渲染模板:就像Vue那样,把数据传给 index.art 这个文件的 {{ msg }} 里·
// 1. 拼接模板路径
// 2. 拼接模板后缀
// 3. 哪个模板和哪个数据进行绑定
// 4. 将拼接结果响应给客户端
res.render('index', {
name: 'alsritter',
gender: 'male',
age: 20
});
})
console.log('启动服务器');
app.listen(3000)
index.art
文件的内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>{{name}}</h1>
<h1>{{gender}}</h1>
<h1>{{age}}</h1>
</body>
</html>
app.locals 对象
将变量设置到 app.locals
对象下面,这个数据在所以的模板中都可以获取到
app.locals.users = [
{name: '张三',age: 20},
{name: '李四',age: 18}
]